$LOAD_PATH.unshift File.join(File.dirname(__FILE__),'../..','ext_pr1/lib')
require 'ext_pr1_v4'

#####################################################################
#Typen 1d-Objekte
#
def range1d?(o) o.int_range? end
def shape1d?(o) range1d?(o) or o.union1d? end

# Union1d :: (left,right)
# Shape1d x Shape1d
def_class(:Union1d,[:left,:right]) {
   def invariant?
      shape1d?(left) and shape1d?(right)
   end
}

# trans1d ::= 
# shape x point -> shape
def trans1d(o,point)
   check_pre((shape1d?(o)))
   range1d?(o) ? (trans0d(o.first,point)..trans0d(o.last,point)) : Union1d[trans1d(o.left,point),trans1d(o.right,point)]
end

def trans0d(v1,v2)
  v1+v2
end
#####################################################################
# Typed 2d-Objekte
def shape2d?(o) o.range2d? or o.union2d? end


#Point2d :: (x,y)
# Point1d x Point1d
def_class(:Point2d,[:x,:y]) {
   def invariant?
      ((x.int? and y.int?))
   end
}

 #Range2d :: (x_range,y_range))
 #Range1d x Range1d
 def_class(:Range2d,[:x_range,:y_range]) {
   def invariant?
     ((range1d?(x_range) and range1d?(y_range)))
   end
 }
 
 #Union2d :: (left,right)
 #Shape2d x Shape2d
 def_class(:Union2d,[:left,:right]) {
   def invariant?
     ((shape2d?(left) and shape2d?(right)))
   end
 }
 
#####################################################################
#Typen Shared-Objekte
def point?(o) o.int? or o.point2d? end
def prim_shape?(o) o.int_range? or o.range2d? end
def union_shape?(o) o.union2d? or o.union1d? end
def comp_shape?(o) union_shape?(o) end
def shape?(o) prim_shape?(o) or comp_shape?(o) end
def graph_obj?(o) point?(o) or shape?(o) end

#####################################################################
### Weitere Translationen

#trans2d ::= (shape2d, point2d) :: Shape2d x Point2d ->? Shape2d
def trans2d(o,point)
   check_pre((point.point2d?))
#   o.range2d? ? Range2d[trans1d(o.left,point),trans1d(o.right,point)] : Union2d[trans2d(o.left,point),trans1d(o.right,point)]

    if    o.range2d? then Range2d[trans1d(o.left,point),trans1d(o.right,point)]
    elsif o.union2d? then Union2d[trans2d(o.left,point),trans1d(o.right,point)]
    else  check_pre false
    end

end

# Super Translation
# translate ::= (shape, point) :: Shape x Point ->? Shape
def translate(shape,point)
   shape?(shape) ? trans2d(shape,point) : trans1d(shape,point)
end

#####################################################################
### Boundingboxen

def bounding_range(r1,r2)
    if    range1d?(r1) and range1d?(r2) then (r1.first.min(r2.first))..(r1.last.max(r2.last))  
    elsif r1.range2d? and r2.range2d?   then  Range2d[bounding_range(r1.x_range,r2.x_range),bounding_range(r1.y_range,r2.y_range)]
    else  check_pre((false))
  end
end

  def bounds(shape)
  if    prim_shape?(shape)  then shape
  elsif union_shape?(shape) then bounding_range(bounds(shape.left),bounds(shape.right))
  else  check_pre((false))
  end
end


#####################################################################
### equals

# graph_equal_? ::= (o1,o2) :: GraphObj x GraphObj -> Bool
def graph_equal_?(o1,o2)
  check_pre((graph_obj?(o1) and graph_obj?(o2)))
  if    point1d?(o1) then (point1d?(o2) and o1 == o2)
  elsif o2.point2d?  then (o2.point2d? and o1 == o2)
  elsif range1d?(o1) then (range1d?(o2) and graph_equal_?(o1.x,o2.x) and graph_equal_?(o1.y,o2.y))
  elsif o1.range2d?  then (o2.range2d? and graph_equal_?(o1.x_range,o2.x_range) and graph_equal_?(o1.y_range,o2.y_range))
  elsif o1.union1d?  then (o2.union1d? and graph_equal_?(o1.left,o2.left) and graph_equal_?(o1.right,o2.right))
  elsif o1.union2d?  then (o2.union2d? and graph_equal_?(o1.left,o2.left) and graph_equal_?(o1.right,o2.right))
  else check_pre false    
  end
end
#           
# graph_equal_trans? ::= (o1,o2) :: Any x Any -> Bool
def graph_equal_trans?(o1,o2)
   graph_equal_?( translate(o1,(bounds(o2).left.first-bounds(o1).left.first)), o2)
end

#in? ::= (point,shape) :: Point x Shape -> Bool
def in_shape?(point,shape)
  check_pre(((point?(point) and (range1d?(shape) or shape.union1d?)) or (point.point2d? and (shape.range2d? or shape.union2d?))))
  if    range1d?(shape)  then                  point.in?(shape)
  elsif shape.range2d?   then                  in_shape?(point.x,shape.x_range) and in_shape?(point.y,shape.y_range)
  elsif shape.union1d? or shape.union2d?  then in_shape?(point,shape.left) or in_shape?(point,shape.right)
  else check_pre false
  end
end
 
  